package com.xbts.screen.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xbts.common.core.domain.R;
import com.xbts.common.core.enums.ConsumeTypeEnum;
import com.xbts.common.core.enums.DateTypeEnum;
import com.xbts.common.core.enums.VirtualDataTypeEnum;
import com.xbts.common.core.utils.DateUtils;
import com.xbts.common.core.utils.StringUtils;
import com.xbts.common.core.web.domain.BaseHistogramResult;
import com.xbts.common.core.web.domain.BasePieChartResult;
import com.xbts.common.core.web.domain.BaseRankingResult;
import com.xbts.screen.domain.WeVirtualData;
import com.xbts.screen.domain.vo.ScreenConsumeHistogramVO;
import com.xbts.screen.domain.vo.ScreenQualityDetailsVO;
import com.xbts.screen.mapper.WeVirtualDataMapper;
import com.xbts.screen.service.IWeVirtualDataService;
import com.xbts.system.api.RemoteFileService;
import com.xbts.system.api.domain.SysFile;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 虚拟数据导入Service业务层处理
 * 
 * @author xbts
 * @date 2024-01-02
 */
@Service
public class WeVirtualDataServiceImpl extends ServiceImpl<WeVirtualDataMapper, WeVirtualData> implements IWeVirtualDataService {

    @Autowired
    private WeVirtualDataMapper weVirtualDataMapper;

    @Autowired
    private RemoteFileService remoteFileService;

    @Transactional
    @Override
    public String importData(MultipartFile file) {
        List<WeVirtualData> list = new ArrayList<>();
        try (InputStream inputStream = file.getInputStream();
             Workbook workbook = new XSSFWorkbook(inputStream)) {
            // 遍历所有Sheet
            int numberOfSheets = workbook.getNumberOfSheets();
            for (int i = 0; i < numberOfSheets; i++) {
                Sheet sheet = workbook.getSheetAt(i);
                String keyByValue = VirtualDataTypeEnum.getKeyByValue(sheet.getSheetName());
                int j = 0;
                // 遍历每一行
                for (Row row : sheet) {
                    if (j++ <= 1)  continue;
                    WeVirtualData weVirtualData = new WeVirtualData();
                    weVirtualData.setType(keyByValue);
                    for (int k = 0; k < 9; k++) {
                        Cell cell = row.getCell(k);
                        if (cell == null) break;
                        if (cell.getCellType() == CellType.BLANK) break;
                        String value = "";
                        switch (cell.getCellType()) {
                            case STRING:
                                value = cell.getStringCellValue();
                                break;
                            case NUMERIC:
                                value = cell.getNumericCellValue() + "";
                                break;
                            case BOOLEAN:
                                value = cell.getBooleanCellValue() + "";
                                break;
                            case FORMULA:
                                value = cell.getCellFormula() + "";
                                break;
                        }
                        Method[] methods = weVirtualData.getClass().getMethods();
                        for (Method method : methods) {
                            if (method.getName().equals("setRow" + (k + 1))) {
                                method.invoke(weVirtualData, value);
                            }
                        }
                    }
                    if(StringUtils.isNotBlank(weVirtualData.getRow1()) || StringUtils.isNotBlank(weVirtualData.getRow2())){
                        list.add(weVirtualData);
                    }
                }
            }
            List<String> typeList = list.stream().map(WeVirtualData::getType).collect(Collectors.toList());
            this.remove(new LambdaQueryWrapper<WeVirtualData>().in(WeVirtualData::getType, typeList));
            this.saveBatch(list);
        } catch (IOException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        R<SysFile> fileResult = remoteFileService.upload(file);
        if (StringUtils.isNull(fileResult) || StringUtils.isNull(fileResult.getData())) {
            return "导入成功";
        }
        String url = fileResult.getData().getUrl();
        weVirtualDataMapper.updateLastFileUrl(url);
        return "导入成功！";
    }

    @Override
    public BaseHistogramResult screenBusinessDetails() {
        List<WeVirtualData> virtualDataList = getByType(VirtualDataTypeEnum.SCREEN_BUSINESS_DETAILS.getKey());
        if (virtualDataList == null || virtualDataList.size() == 0) return null;

        List<String> lastTwelveMonths = DateUtils.getLastTwelveMonths();
        return getBaseHistogramResultByRow2AndRow3(virtualDataList, lastTwelveMonths);
    }

    @Override
    public BaseHistogramResult screenCapacityDetails() {
        List<WeVirtualData> virtualDataList = getByType(VirtualDataTypeEnum.SCREEN_CAPACITY_DETAILS.getKey());
        if (virtualDataList == null || virtualDataList.size() == 0) return null;

        List<String> lastSixMonths = DateUtils.getLastSixMonths();
        return getBaseHistogramResultByRow2AndRow3(virtualDataList, lastSixMonths);
    }


    @Override
    public List<BasePieChartResult> screenCostDetailsA() {
        List<WeVirtualData> virtualDataList = getByType(VirtualDataTypeEnum.SCREEN_COST_DETAILS_A.getKey());
        if (virtualDataList == null || virtualDataList.size() == 0) return null;
        return virtualDataList.stream().map(v -> {
            BasePieChartResult basePieChartResult = new BasePieChartResult();
            basePieChartResult.setLabel(v.getRow1());
            basePieChartResult.setType(v.getRow2());
            basePieChartResult.setValue(v.getRow3());
            return basePieChartResult;
        }).collect(Collectors.toList());
    }

    @Override
    public BaseHistogramResult screenCostDetailsB() {
        List<WeVirtualData> virtualDataList = getByType(VirtualDataTypeEnum.SCREEN_COST_DETAILS_B.getKey());
        if (virtualDataList == null || virtualDataList.size() == 0) return null;
        List<String> lastTwelveMonths = DateUtils.getLastTwelveMonths();
        return getBaseHistogramResultByRow2AndRow3(virtualDataList, lastTwelveMonths);
    }

    @Override
    public ScreenConsumeHistogramVO screenConsumeDetails() {
        List<WeVirtualData> virtualDataList = getByType(VirtualDataTypeEnum.SCREEN_CONSUME_DETAILS.getKey());
        if (virtualDataList == null || virtualDataList.size() == 0) return null;
        List<String> lastTenDays = DateUtils.getLastTenDays();
        ScreenConsumeHistogramVO result = new ScreenConsumeHistogramVO();
        result.setXAxis(lastTenDays);
        result.setWaterTarget(virtualDataList.stream().map(WeVirtualData::getRow3).collect(Collectors.toList()));
        result.setWaterActual(virtualDataList.stream().map(WeVirtualData::getRow2).collect(Collectors.toList()));
        result.setElectricityTarget(virtualDataList.stream().map(WeVirtualData::getRow4).collect(Collectors.toList()));
        result.setElectricityActual(virtualDataList.stream().map(WeVirtualData::getRow5).collect(Collectors.toList()));
        return result;
    }

    @Override
    public List<ScreenQualityDetailsVO> screenQualityDetails() {
        List<WeVirtualData> virtualDataList = getByType(VirtualDataTypeEnum.SCREEN_QUALITY_DETAILS.getKey());
        if (virtualDataList == null || virtualDataList.size() == 0) return null;

        return virtualDataList.stream().map(v -> {
            ScreenQualityDetailsVO vo = new ScreenQualityDetailsVO();
            vo.setLastTime(v.getRow2());
            vo.setThisTime(v.getRow3());
            vo.setTarget(v.getRow4());
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public BaseHistogramResult screenQualityAnalyseA() {
        List<WeVirtualData> virtualDataList = getByType(VirtualDataTypeEnum.SCREEN_QUALITY_ANALYSE_A.getKey());
        if (virtualDataList == null || virtualDataList.size() == 0) return null;

        List<String> lastSixMonths = DateUtils.getLastSixMonths();
        return getBaseHistogramResultByRow2AndRow3(virtualDataList, lastSixMonths);
    }

    @Override
    public BaseHistogramResult screenQualityAnalyseB() {
        List<WeVirtualData> virtualDataList = getByType(VirtualDataTypeEnum.SCREEN_QUALITY_ANALYSE_B.getKey());
        if (virtualDataList == null || virtualDataList.size() == 0) return null;

        List<String> xAxis = DateUtils.getPastThreeMonths();
        xAxis.addAll(DateUtils.getLastFourWeeks());
        xAxis.addAll(DateUtils.getLastSevenDays());
        return getBaseHistogramResultByRow2AndRow3(virtualDataList, xAxis);
    }

    @Override
    public BaseHistogramResult businessDetailsByDateType(DateTypeEnum dateType) {
        BaseHistogramResult result;
        List<WeVirtualData> virtualDataList;
        switch (dateType) {
            case DAY:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_DAY_DETAILS.getKey());
                result = getBaseHistogramResultByRow2AndRow3(virtualDataList, DateUtils.getDaysOfMonth());
                baseHistogramResultRemoveExcess(result);
                break;
            case WEEK:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_WEEK_DETAILS.getKey());
                result = getBaseHistogramResultByRow2AndRow3(virtualDataList, DateUtils.getLastEightWeeks());
                break;
            case MONTH:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_MONTH_DETAILS.getKey());
                result = getBaseHistogramResultByRow2AndRow3(virtualDataList, DateUtils.getLastTwelveMonths());
                break;
            default:
                return null;
        }
        return result;
    }

    @Override
    public List<BasePieChartResult> businessStatistics(DateTypeEnum dateType) {
        List<WeVirtualData> virtualDataList;
        switch (dateType) {
            case DAY:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_DAY_STATISTICS.getKey());
                break;
            case WEEK:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_WEEK_STATISTICS.getKey());
                break;
            case MONTH:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_MONTH_STATISTICS.getKey());
                break;
            default:
                return null;
        }
        return virtualDataList.stream().map(v -> {
            BasePieChartResult vo = new BasePieChartResult();
            vo.setType(v.getRow2());
            vo.setValue(v.getRow3());
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<BaseRankingResult> businessRanking(DateTypeEnum dateType) {
        List<WeVirtualData> virtualDataList;
        switch (dateType) {
            case DAY:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_DAY_RANKING.getKey());
                break;
            case WEEK:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_WEEK_RANKING.getKey());
                break;
            case MONTH:
                virtualDataList = getByType(VirtualDataTypeEnum.BUSINESS_MONTH_RANKING.getKey());
                break;
            default:
                return null;
        }
        return virtualDataList.stream().map(v -> {
            BaseRankingResult vo = new BaseRankingResult();
            vo.setNumber(Integer.valueOf(v.getRow1()));
            vo.setName(v.getRow2());
            vo.setValue(v.getRow3());
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public BaseHistogramResult capacityDetailsByDateType(DateTypeEnum dateType) {
        List<WeVirtualData> virtualDataList;
        switch (dateType) {
            case DAY:
                virtualDataList = getByType(VirtualDataTypeEnum.CAPACITY_DAY_DETAILS.getKey());
                break;
            case WEEK:
                virtualDataList = getByType(VirtualDataTypeEnum.CAPACITY_WEEK_DETAILS.getKey());
                break;
            case MONTH:
                virtualDataList = getByType(VirtualDataTypeEnum.CAPACITY_MONTH_DETAILS.getKey());
                break;
            default:
                return null;
        }
        BaseHistogramResult result = new BaseHistogramResult();
        result.setXAxis(virtualDataList.stream().map(WeVirtualData::getRow1).collect(Collectors.toList()));
        result.setTarget(virtualDataList.stream().map(WeVirtualData::getRow2).collect(Collectors.toList()));
        result.setActual(virtualDataList.stream().map(WeVirtualData::getRow3).collect(Collectors.toList()));
        return result;
    }

    @Override
    public BaseHistogramResult consumeDetailsByDateType(DateTypeEnum dateType, ConsumeTypeEnum consumeType) {
        List<WeVirtualData> virtualDataList;
        if (consumeType.equals(ConsumeTypeEnum.WATER)) {
            switch (dateType) {
                case DAY:
                    virtualDataList = getByType(VirtualDataTypeEnum.CONSUME_WATER_DAY_DETAILS.getKey());
                    break;
                case WEEK:
                    virtualDataList = getByType(VirtualDataTypeEnum.CONSUME_WATER_WEEK_DETAILS.getKey());
                    break;
                case MONTH:
                    virtualDataList = getByType(VirtualDataTypeEnum.CONSUME_WATER_MONTH_DETAILS.getKey());
                    break;
                default:
                    return null;
            }
        } else if (consumeType.equals(ConsumeTypeEnum.ELECTRICITY)) {
            switch (dateType) {
                case DAY:
                    virtualDataList = getByType(VirtualDataTypeEnum.CONSUME_ELECTRICITY_DAY_DETAILS.getKey());
                    break;
                case WEEK:
                    virtualDataList = getByType(VirtualDataTypeEnum.CONSUME_ELECTRICITY_WEEK_DETAILS.getKey());
                    break;
                case MONTH:
                    virtualDataList = getByType(VirtualDataTypeEnum.CONSUME_ELECTRICITY_MONTH_DETAILS.getKey());
                    break;
                default:
                    return null;
            }
        } else {
            return null;
        }
        BaseHistogramResult result = new BaseHistogramResult();
        result.setXAxis(virtualDataList.stream().map(WeVirtualData::getRow1).collect(Collectors.toList()));
        result.setTarget(virtualDataList.stream().map(WeVirtualData::getRow2).collect(Collectors.toList()));
        result.setActual(virtualDataList.stream().map(WeVirtualData::getRow3).collect(Collectors.toList()));
        return result;
    }

    @Override
    public List<BaseHistogramResult> consumeOverallDetailsByDateType(DateTypeEnum dateType) {
        List<BaseHistogramResult> qualityBaseHistogramResults;
        List<WeVirtualData> virtualDataList;
        switch (dateType) {
            case DAY:
                virtualDataList = getByType(VirtualDataTypeEnum.QUALITY_OVERALL_DAY_DETAILS.getKey());
                qualityBaseHistogramResults = getQualityBaseHistogramResults(virtualDataList);
                qualityBaseHistogramResults.forEach(this::baseHistogramResultRemoveExcess);
                break;
            case WEEK:
                virtualDataList = getByType(VirtualDataTypeEnum.QUALITY_OVERALL_WEEK_DETAILS.getKey());
                qualityBaseHistogramResults = getQualityBaseHistogramResults(virtualDataList);
                qualityBaseHistogramResults.forEach(result -> {
                    result.setXAxis(DateUtils.getLastEightWeeks());
                });
                break;
            case MONTH:
                virtualDataList = getByType(VirtualDataTypeEnum.QUALITY_OVERALL_MONTH_DETAILS.getKey());
                qualityBaseHistogramResults = getQualityBaseHistogramResults(virtualDataList);
                qualityBaseHistogramResults.forEach(result -> {
                    result.setXAxis(DateUtils.getLastTwelveMonths());
                });
                break;
            default:
                return null;
        }
        return qualityBaseHistogramResults;
    }

    @Override
    public List<BaseHistogramResult> qualityWorkshopOverallDetailsByDateType(DateTypeEnum dateType) {
        List<WeVirtualData> virtualDataList;
        switch (dateType) {
            case DAY:
                virtualDataList = getByType(VirtualDataTypeEnum.QUALITY_WORKSHOP_DAY_DETAILS.getKey());
                break;
            case WEEK:
                virtualDataList = getByType(VirtualDataTypeEnum.QUALITY_WORKSHOP_WEEK_DETAILS.getKey());
                break;
            case MONTH:
                virtualDataList = getByType(VirtualDataTypeEnum.QUALITY_WORKSHOP_MONTH_DETAILS.getKey());
                break;
            default:
                return null;
        }
        return getQualityBaseHistogramResults(virtualDataList);
    }

    @Override
    public BaseHistogramResult costOverallDetailsByDateType(DateTypeEnum dateType) {
        BaseHistogramResult result;
        List<WeVirtualData> virtualDataList;
        switch (dateType) {
            case DAY:
                virtualDataList = getByType(VirtualDataTypeEnum.COST_OVERALL_DAY_DETAILS.getKey());
                result = getBaseHistogramResultByRow2AndRow3(virtualDataList, DateUtils.getDaysOfMonth());
                baseHistogramResultRemoveExcess(result);
                break;
            case WEEK:
                virtualDataList = getByType(VirtualDataTypeEnum.COST_OVERALL_WEEK_DETAILS.getKey());
                result = getBaseHistogramResultByRow2AndRow3(virtualDataList, DateUtils.getLastEightWeeks());
                break;
            case MONTH:
                virtualDataList = getByType(VirtualDataTypeEnum.COST_OVERALL_MONTH_DETAILS.getKey());
                result = getBaseHistogramResultByRow2AndRow3(virtualDataList, DateUtils.getLastTwelveMonths());
                break;
            default:
                return null;
        }
        return result;
    }

    @Override
    public BaseHistogramResult costWorkshopOverallDetailsByDateType(DateTypeEnum dateType) {
        BaseHistogramResult result = new BaseHistogramResult();
        List<WeVirtualData> virtualDataList;
        switch (dateType) {
            case DAY:
                virtualDataList = getByType(VirtualDataTypeEnum.COST_WORKSHOP_DAY_DETAILS.getKey());
                break;
            case WEEK:
                virtualDataList = getByType(VirtualDataTypeEnum.COST_WORKSHOP_WEEK_DETAILS.getKey());
                break;
            case MONTH:
                virtualDataList = getByType(VirtualDataTypeEnum.COST_WORKSHOP_MONTH_DETAILS.getKey());
                break;
            default:
                return null;
        }
        result.setXAxis(virtualDataList.stream().map(WeVirtualData::getRow1).collect(Collectors.toList()));
        result.setTarget(virtualDataList.stream().map(WeVirtualData::getRow2).collect(Collectors.toList()));
        result.setActual(virtualDataList.stream().map(WeVirtualData::getRow3).collect(Collectors.toList()));
        return result;
    }

    @Override
    public BaseHistogramResult costWorkshopOverallDetailsByDateTypeAndConsumeType(DateTypeEnum dateType, ConsumeTypeEnum consumeType) {
        List<WeVirtualData> virtualDataList = getByType("cost_workshop_" + consumeType.name().toLowerCase() + "_" + dateType.name().toLowerCase() + "_details");
        if (virtualDataList == null || virtualDataList.isEmpty()) {
            return null;
        }
        BaseHistogramResult result = new BaseHistogramResult();
        result.setXAxis(virtualDataList.stream().map(WeVirtualData::getRow1).collect(Collectors.toList()));
        result.setTarget(virtualDataList.stream().map(WeVirtualData::getRow2).collect(Collectors.toList()));
        result.setActual(virtualDataList.stream().map(WeVirtualData::getRow3).collect(Collectors.toList()));
        return result;
    }

    @Override
    public Map<String, String> template() {
        Map<String, String> map = new HashMap<>();
        map.put("templateUrl", weVirtualDataMapper.getTemplateUrl());
        map.put("lastFileUrl", weVirtualDataMapper.getLastFileUrl());
        return map;
    }

    private List<BaseHistogramResult> getQualityBaseHistogramResults(List<WeVirtualData> virtualDataList) {
        List<BaseHistogramResult> resultList = new ArrayList<>();
        List<String> axis = virtualDataList.stream().map(WeVirtualData::getRow1).collect(Collectors.toList());
        BaseHistogramResult yieldRate = new BaseHistogramResult();
        yieldRate.setXAxis(axis);
        yieldRate.setTarget(virtualDataList.stream().map(WeVirtualData::getRow2).collect(Collectors.toList()));
        yieldRate.setActual(virtualDataList.stream().map(WeVirtualData::getRow3).collect(Collectors.toList()));
        resultList.add(yieldRate);
        BaseHistogramResult scrapRate = new BaseHistogramResult();
        scrapRate.setXAxis(axis);
        scrapRate.setTarget(virtualDataList.stream().map(WeVirtualData::getRow4).collect(Collectors.toList()));
        scrapRate.setActual(virtualDataList.stream().map(WeVirtualData::getRow5).collect(Collectors.toList()));
        resultList.add(scrapRate);
        BaseHistogramResult customerComplaintRate = new BaseHistogramResult();
        customerComplaintRate.setXAxis(axis);
        customerComplaintRate.setTarget(virtualDataList.stream().map(WeVirtualData::getRow6).collect(Collectors.toList()));
        customerComplaintRate.setActual(virtualDataList.stream().map(WeVirtualData::getRow7).collect(Collectors.toList()));
        resultList.add(customerComplaintRate);
        return resultList;
    }


    private List<WeVirtualData> getByType(String type) {
        if (StringUtils.isEmpty(type)) return null;
        return this.list(new LambdaQueryWrapper<WeVirtualData>()
                .eq(WeVirtualData::getType, type));
    }

    private BaseHistogramResult getBaseHistogramResultByRow2AndRow3(List<WeVirtualData> virtualDataList, List<String> xais) {
        BaseHistogramResult result = new BaseHistogramResult();
        result.setXAxis(xais);
        result.setTarget(virtualDataList.stream().map(WeVirtualData::getRow2).collect(Collectors.toList()));
        result.setActual(virtualDataList.stream().map(WeVirtualData::getRow3).collect(Collectors.toList()));
        return result;
    }

    private List<String> replaceValuesFromIndex(List<String> list, int index, String newValue) {
        // 确保下标在有效范围内
        if (index >= 0 && index < list.size()) {
            // 从指定下标开始，替换到列表末尾
            for (int i = index; i < list.size(); i++) {
                list.set(i, newValue);
            }
        } else {
            System.out.println("指定的下标超出范围.");
        }
        return list;
    }

    private BaseHistogramResult baseHistogramResultRemoveExcess(BaseHistogramResult result) {
        // 获取今天是本月的第几天
        int dayOfMonth = LocalDate.now().getDayOfMonth();
        result.setXAxis(DateUtils.getDaysOfMonth());
        result.setTarget(replaceValuesFromIndex(result.getTarget().subList(0, DateUtils.lengthOfMonth()), dayOfMonth, "0"));
        result.setActual(replaceValuesFromIndex(result.getActual().subList(0, DateUtils.lengthOfMonth()), dayOfMonth, "0"));
        return result;
    }

}
